/**
 * API重试策略工具
 */

/**
 * 重试选项接口
 */
export interface RetryOptions {
    /** 最大重试次数 */
    maxRetries?: number;
    /** 初始延迟时间(ms) */
    initialDelay?: number;
    /** 最大延迟时间(ms) */
    maxDelay?: number;
    /** 是否在延迟时间上添加随机抖动 */
    jitter?: boolean;
    /** 可重试的HTTP状态码 */
    retryableStatusCodes?: number[];
    /** 自定义重试条件 */
    retryCondition?: (error: any) => boolean;
}

/**
 * 重试策略类
 */
export class RetryStrategy {
    private readonly defaultOptions: Required<RetryOptions> = {
        maxRetries: 3,
        initialDelay: 1000,
        maxDelay: 30000,
        jitter: true,
        retryableStatusCodes: [408, 429, 500, 502, 503, 504],
        retryCondition: () => true
    };

    private options: Required<RetryOptions>;

    /**
     * 创建重试策略实例
     * @param options 重试选项
     */
    constructor(options: RetryOptions = {}) {
        this.options = { ...this.defaultOptions, ...options };
    }

    /**
     * 使用重试策略执行函数
     * @param fn 要执行的函数
     * @returns 执行结果Promise
     */
    async execute<T>(fn: () => Promise<T>): Promise<T> {
        let attempts = 0;
        let lastError: any;

        while (attempts <= this.options.maxRetries) {
            try {
                return await fn();
            } catch (error) {
                lastError = error;

                // 检查是否达到最大重试次数
                if (attempts >= this.options.maxRetries) {
                    break;
                }

                // 检查错误是否可重试
                const isRetryable = this.isRetryableError(error);
                if (!isRetryable) {
                    break;
                }

                // 计算延迟时间
                const delay = this.calculateDelay(attempts);

                // 记录重试信息
                console.log(`请求失败，将在${delay}ms后进行第${attempts + 1}次重试`, error);

                // 等待后重试
                await this.sleep(delay);
                attempts++;
            }
        }

        throw lastError;
    }

    /**
     * 判断错误是否可重试
     * @param error 错误对象
     * @returns 是否可重试
     */
    private isRetryableError(error: any): boolean {
        // 检查HTTP状态码
        const status = error.status || (error.response && error.response.status);
        if (status && this.options.retryableStatusCodes.includes(status)) {
            return true;
        }

        // 网络连接错误
        if (error instanceof TypeError && error.message.includes('fetch')) {
            return true;
        }

        // 连接重置错误
        if (error.code === 'ECONNRESET' || error.code === 'ETIMEDOUT') {
            return true;
        }

        // 自定义重试条件
        return this.options.retryCondition(error);
    }

    /**
     * 计算重试延迟时间
     * @param attempt 当前尝试次数
     * @returns 延迟时间(ms)
     */
    private calculateDelay(attempt: number): number {
        // 指数退避: initialDelay * 2^attempt
        let delay = this.options.initialDelay * Math.pow(2, attempt);

        // 限制最大延迟时间
        delay = Math.min(delay, this.options.maxDelay);

        // 添加随机抖动(±30%)
        if (this.options.jitter) {
            const jitterRange = delay * 0.3;
            delay = delay - jitterRange + (Math.random() * jitterRange * 2);
        }

        return Math.floor(delay);
    }

    /**
     * 睡眠指定时间
     * @param ms 毫秒数
     * @returns Promise
     */
    private sleep(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
} 